/*
 * OPIAM Suite
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package opiam.admin.faare.admin;

import opiam.admin.faare.config.javabeans.JBFieldDescriptor;
import opiam.admin.faare.config.javabeans.JBProfile;
import opiam.admin.faare.config.javabeans.JBProfiles;
import opiam.admin.faare.config.javabeans.JBRessource;
import opiam.admin.faare.config.javabeans.PersistenceDescriptionMap;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;

import java.util.Iterator;
import java.util.Map;
import java.util.Properties;


/**
 * Main class for JavaBeans generator tool.
 */
public class FaareMappingBeansGenerator
{
    /** Instance of logger. */
    private static Logger _logger = Logger.getLogger(FaareMappingBeansGenerator.class);

    /** Fichier properties pour log4j. */
    private static final String LOG4J_PROPS_FILE = "/config/core/logger_config.properties";

    /** Fichier de mapping LDAP -> objet. */
    private static final String FAARE_MAPPING_FILE = "/config/core/faare_mapping.xml";

    /** Fichier de definition des ressources et des profils. */
    private static final String PROFILES_CONFIG_FILE = "/config/core/profiles_config.xml";

    /** Objets charges de faare_mapping.xml. */
    private static PersistenceDescriptionMap persistenceDescriptionMap;

    /** Objets charges de profiles_config.xml. */
    private static JBProfiles profiles;

    /** Nom de la classe utilisee comme ressource logon. */
    private static String logonClass = "";

    /**
     * Initialisation de log4j.
     * @param directory : Conf directory.
     */
    public void initialize(String directory)
    {
        try
        {
            Properties log4jProps = new Properties();

            if (directory == null)
            {
                log4jProps.load(this.getClass().getResourceAsStream(LOG4J_PROPS_FILE));
            }
            else
            {
                File f = new File(directory + LOG4J_PROPS_FILE);
                log4jProps.load(new FileInputStream(f));
            }

            PropertyConfigurator.configure(log4jProps);
        }
        catch (Exception ex)
        {
            _logger.error("***** Erreur d'initialisation " + ex.getMessage());
        }
    }

    /**
     * Chargement des fichiers faare_mapping.xml et profiles_config.xml.
     * @param directory : Conf directory.
     */
    public void loadMapping(String directory)
    {
        try
        {
            if (directory == null)
            {
                persistenceDescriptionMap = PersistenceConfig.getInstance()
                                                             .readConfig(this.getClass()
                                                                             .getResourceAsStream(FAARE_MAPPING_FILE));
                profiles = ProfilesConfig.getInstance().readConfig(this.getClass()
                                                                       .getResourceAsStream(PROFILES_CONFIG_FILE));
            }
            else
            {
                File f = new File(directory + FAARE_MAPPING_FILE);
                persistenceDescriptionMap = PersistenceConfig.getInstance()
                                                             .readConfig(new FileInputStream(
                            f));

                f = new File(directory + PROFILES_CONFIG_FILE);
                profiles = ProfilesConfig.getInstance().readConfig(new FileInputStream(
                            f));
            }

            /* recherche de la ressource logon */
            JBRessource logonResource = profiles.findRessourceByName("logon");

            if (logonResource == null)
            {
                _logger.error("Pas de ressource logon ?");
            }
            else
            {
                logonClass = logonResource.getType();
                _logger.info("logon class is " + logonClass);
            }
        }
        catch (Exception ex)
        {
            _logger.error("***** Erreur de chargement " + ex.getMessage());
            ex.printStackTrace();
        }
    }

    /**
     * Affichage des objets lus dans faare_mapping.xml.
     */
    public void liste()
    {
        try
        {
            Iterator classnames = persistenceDescriptionMap.getDescMap().keySet()
                                                           .iterator();

            while (classnames.hasNext())
            {
                String classname = (String) classnames.next();
                opiam.admin.faare.config.javabeans.JBClassDescriptor beanclass =
                    persistenceDescriptionMap.getClassDescriptor(classname);
                _logger.debug(".." + classname);

                Iterator fields = beanclass.getFields().iterator();

                while (fields.hasNext())
                {
                    JBFieldDescriptor field = (JBFieldDescriptor) fields.next();

                    if (!field.getName().equals(beanclass.getIdentity()))
                    {
                        _logger.debug(".... nom : " + field.getName());
                        _logger.debug("...... type : " + field.getType());

                        if (field.isACollection())
                        {
                            _logger.debug("...... collection : " +
                                field.getCollection());
                        }

                        if (field.isALazy())
                        {
                            _logger.debug("...... lazy : " + field.getLazy());
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            _logger.error("***** Erreur de liste " + ex.getMessage());
        }
    }

    /**
     * Fonction qui verifie que la liste de ressources existe.
     * @param ressMap Resources to check.
     * @return true is all resources exist, false if at least one resource does not exist.
     */
    private boolean verifieRessMap(Map ressMap)
    {
        if (ressMap == null)
        {
            return true;
        }

        Iterator ressources = ressMap.values().iterator();

        while (ressources.hasNext())
        {
            JBRessource ress = (JBRessource) ressources.next();

            if (persistenceDescriptionMap.getDescMap().get(ress.getType()) == null)
            {
                _logger.error("***** ressource " + ress.getName() +
                    " inconnue");

                return false;
            }
        }

        return true;
    }

    /**
     * Fonction qui verifie que la liste de ressources de la liste de profils existe.
     * @param profilesMap Profiles to check.
     * @return true is all resources exist, false if at least one resource does not exist.
     */
    private boolean verifieProfilesMap(Map profilesMap)
    {
        Iterator profs = profilesMap.values().iterator();

        while (profs.hasNext())
        {
            JBProfile prof = (JBProfile) profs.next();

            if ((prof.getJbRessources() != null) &&
                    (!verifieRessMap(prof.getJbRessources().getRessourceMap())))
            {
                return false;
            }
        }

        return true;
    }

    /**
     * Verification de coherence des donnees lues.
     * @return True if OK, false if error.
     */
    public boolean verifie()
    {
        try
        {
            /*
             * verification sur faare_mapping.xml : on verifie simplement que les
             * types sont connus. Ce sont soit des types java simples, soit des objets
             * metier qui existent.
             */
            Iterator classnames = persistenceDescriptionMap.getDescMap().keySet()
                                                           .iterator();

            while (classnames.hasNext())
            {
                String classname = (String) classnames.next();

                if (classname.startsWith(
                            "opiam.admin.faare.examples.tutorial"))
                {
                    continue;
                }

                opiam.admin.faare.config.javabeans.JBClassDescriptor beanclass =
                    persistenceDescriptionMap.getClassDescriptor(classname);
                Iterator fields = beanclass.getFields().iterator();

                while (fields.hasNext())
                {
                    JBFieldDescriptor field = (JBFieldDescriptor) fields.next();
                    String fieldtype = field.getType();

                    /* type Java : OK */
                    if (fieldtype.startsWith("java.lang."))
                    {
                        continue;
                    }

                    if (persistenceDescriptionMap.getDescMap().get(fieldtype) == null)
                    {
                        _logger.error("***** type " + fieldtype +
                            " inconnu pour " + classname);

                        return false;
                    }
                }
            }

            /*
             * verification sur profiles_config.xml : on verifie simplement que les
             * ressources sont definies dans faare_mapping.xml.
             */
            boolean result = verifieRessMap(profiles.getRessourcesMap());

            if (result)
            {
                result = verifieProfilesMap(profiles.getProfilesDefaultMap());
            }

            if (result)
            {
                result = verifieProfilesMap(profiles.getProfilesDynamicGroupMap());
            }

            if (result)
            {
                result = verifieProfilesMap(profiles.getProfilesFilterMap());
            }

            if (result)
            {
                result = verifieProfilesMap(profiles.getProfilesStaticGroupMap());
            }

            if (result)
            {
                result = verifieProfilesMap(profiles.getProfilesUserMap());
            }

            return result;
        }
        catch (Exception ex)
        {
            _logger.error("***** Erreur de verification " + ex.getMessage());
            ex.printStackTrace();
        }

        /* il y a eu exception => erreur */
        return false;
    }

    /** Generation des JavaBeans a partir des donnees lues dans faare_mapping.xml. */
    public void genere()
    {
        try
        {
            Iterator classnames = persistenceDescriptionMap.getDescMap().keySet()
                                                           .iterator();

            while (classnames.hasNext())
            {
                String classname = (String) classnames.next();

                /* on ne regenere pas les classes du tutorial */
                if (classname.startsWith(
                            "opiam.admin.faare.examples.tutorial"))
                {
                    continue;
                }

                opiam.admin.faare.config.javabeans.JBClassDescriptor beanclass =
                    persistenceDescriptionMap.getClassDescriptor(classname);

                int pos = classname.lastIndexOf(".");
                String pkgname = classname.substring(0, pos);
                String clsname = classname.substring(pos + 1);
                FileWriter outfile = new FileWriter("output/" + clsname +".java");
                outfile.write("package " + pkgname + ";\n\n");
                outfile.write("import java.util.Collection;\n\n");

                String father = "JBTop";

                /* la classe logon herite de JBUser plutot que de JBTop */
                if (classname.equals(logonClass))
                {
                    father = "JBUser";
                }

                outfile.write(
                    "import opiam.admin.faare.persistence.javabeans." +
                    father + ";\n\n");
                outfile.write(
                    "/*\n * Classe generee par OPIAM-Admin\n */\n\n");
                outfile.write("public class " + clsname + " extends " + father +
                    " implements java.io.Serializable\n{\n");

                Iterator fields = beanclass.getFields().iterator();

                while (fields.hasNext())
                {
                    JBFieldDescriptor field = (JBFieldDescriptor) fields.next();

                    /* pas de champ genere pour celui qui identifie la classe, il est dans JBTop */
                    if (!field.getName().equals(beanclass.getIdentity()))
                    {
                        String nom = field.getName().substring(0, 1)
                                          .toUpperCase() +
                            field.getName().substring(1);
                        String fieldtype = field.getType();
                        String eol = ";\n\n";

                        if (field.isACollection())
                        {
                            fieldtype = "Collection";

                            //                     eol = " = new ArrayList();\n\n";
                        }

                        outfile.write("   private " + fieldtype + " " +
                            field.getName() + eol);
                        outfile.write("   public " + fieldtype + " get" + nom +
                            "()\n");
                        outfile.write("   {\n      return " + field.getName() +
                            ";\n   }\n\n");
                        outfile.write("   public void set" + nom + "(" +
                            fieldtype + " value)\n");
                        outfile.write("   {\n      this." + field.getName() +
                            "=value;\n   }\n\n");
                    }
                }

                outfile.write("}\n");
                outfile.close();
            }
        }
        catch (Exception ex)
        {
            _logger.error("***** Erreur de generation " + ex.getMessage());
        }
    }

    /**
     * Main class for the tool.
     * @param args command lines arguments (none).
     */
    public static void main(String[] args)
    {
        FaareMappingBeansGenerator fbg = new FaareMappingBeansGenerator();
        String directory = null;

        if (args.length != 0)
        {
            directory = args[0];
        }

        fbg.initialize(directory);
        fbg.loadMapping(directory);

        //      fbg.liste ();
        if (fbg.verifie())
        {
            fbg.genere();
        }
    }
}
